package org.helgoboss.hotkey_listener

import org.helgoboss.module_support.{ModuleContext, Module}
import com.melloware.jintellitype.{JIntellitype, HotkeyListener => JHotkeyListener, JIntellitypeConstants}

/**
 * Very simple object for reacting to hotkeys.
 */
object HotkeyListener {
    /* Add only one jIntellitype HotKeyListener for this singleton */
    jIntellitype.addHotKeyListener(myJHotkeyListener)
    
    private class HotkeyConfig(val jIntellitypeId: Int, val hotkey: Hotkey, val reaction: () => Unit)
    
    private val hotkeyConfigByJIntellitypeId = new collection.mutable.HashMap[Int, HotkeyConfig]
    private val hotkeyConfigByHotkey = new collection.mutable.HashMap[Hotkey, HotkeyConfig]
    private val usedJIntellitypeIds = new collection.mutable.HashSet[Int]
    
    private object myJHotkeyListener extends JHotkeyListener {
        def onHotKey(id: Int) {
            hotkeyConfigByJIntellitypeId.get(id).foreach { config =>
                config.reaction()
            }
        }
    }

    def update(hotkey: Hotkey, reaction: => Unit) {
        val jIntellitypeId = nextFreeJIntellitypeId
        
        /* Add hotkey config */
        addHotkeyConfig(new HotkeyConfig(jIntellitypeId = jIntellitypeId, hotkey = hotkey, reaction = reaction _))
        
        /* Start listening to this hotkey */   
        jIntellitype.registerHotKey(jIntellitypeId, hotkey.modifierCombination.jIntellitypeRepresentation, hotkey.key.id)
    }
    
    def -=(hotkey: Hotkey) = {
        hotkeyConfigByHotkey.get(hotkey).foreach { hotkeyConfig =>
            /* Stop listening to this hotkey */
            jIntellitype.unregisterHotKey(hotkeyConfig.jIntellitypeId)
                        
            /* Remove hotkey config */
            removeHotkeyConfig(hotkeyConfig)
        }
    }
    
    private def addHotkeyConfig(config: HotkeyConfig) {
        hotkeyConfigByJIntellitypeId(config.jIntellitypeId) = config
        hotkeyConfigByHotkey(config.hotkey) = config
        usedJIntellitypeIds += config.jIntellitypeId
    }
    
    private def removeHotkeyConfig(config: HotkeyConfig) {
        hotkeyConfigByJIntellitypeId -= config.jIntellitypeId
        hotkeyConfigByHotkey -= config.hotkey
        usedJIntellitypeIds -= config.jIntellitypeId
    }
        
    private def nextFreeJIntellitypeId = {
        (1 to Int.MaxValue).find(!usedJIntellitypeIds.contains(_)).get
    }
    
    private def jIntellitype = JIntellitype.getInstance
}

/**
 * Trait which can be mixed in in order to add hotkey listener module support (for example for use in OSGi bundle activators.
 */
trait HotkeyListener extends ModuleContext with ModifiersAndKeys {
    protected implicit def richHotkey(hotkey: Hotkey) = new RichHotkey(hotkey)
    
    protected class RichHotkey(hotkey: Hotkey) {
        def triggers(reaction: => Unit) {
            addModule(new Assignment(hotkey, reaction _))
        }
    }
    
    protected class Assignment(hotkey: Hotkey, reaction: () => Unit) extends Module {
        def start {
            HotkeyListener(hotkey) = {
                reaction()
            }
        }
        
        def stop {
            HotkeyListener -= hotkey
        }
    }
}